// const char *a[] = {"He", "will", "turn into", "first", "in the world!"};
// const char **b[] = {a + 4, a + 3, a + 2, a + 1, a + 0};
// const char ***c = b;
// const char *f1() { return **(c++ + 4); }
// const char *f2() { return *--*++c + 2; }
// const char *f3() { return (*c)[1] + 8; }
// const char *f4() { return c[1][3] + 6; }
// const char *(*F1[])() = {f1, f2, f3, f4, 0};
// const char *(**F2)() = F1;
// #include <stdio.h>
// int main()
// {
//     for (int i = 0; F2[i]; i++)
//     {
//         printf("%s", F2[i]());
//     }
//     return 0;
// }

// a是一个数组，每个元素存放字符串常量的地址
const char *a[] = {"He", "will", "turn into", "first", "in the world!"};

// b同样是一个数组，每个元素存放字符串常量地址的指针
const char **b[] = {a + 4, a + 3, a + 2, a + 1, a + 0};

// c是一根指针，指向字符串常量地址指针
const char ***c = b;

// f1是一个函数，它不接收参数，并返回类型为const char*的表达式**(c++ + 4)的值
// c++这个表达式的结果是b数组第1个元素的地址,但实际上c已经指向了b数组的第2个元素
// c++ + 4 将b数组的第1个元素的地址加4得到b数组的第5个元素的地址
// *(c++ + 4)解引用得到b数组的第5个元素a
// **(c++ + 4)将数组名a解引用得到a数组的第1个元素即字符串常量"He"的地址
const char *f1() { return **(c++ + 4); }

// f2是一个函数，它不接收参数，并返回类型为const char *的表达式*--*++c + 2的值
// 此时的c指向b数组的第2个元素
// ++c使c指向b数组的第3个元素
// * ++c解引用得到b数组的第3个元素即指向a数组的第3个元素的指针
// -- * ++c使b数组的第3个元素指向a数组的第2个元素
// *-- * ++c解引用得到a数组的第2个元素即字符串常量"well"的地址
// *-- * ++c + 2将首字符'w'的地址加2得到第3个字符'l'的地址
const char *f2() { return *--*++c + 2; }

// f3是一个函数，它不接收参数，并返回类型为const char *的表达式(*c)[1] + 8的值
// 此时的c指向b数组的第3个元素，此时b数组的第3个元素指向的是a数组的第2个元素
// (*c)得到b数组的第3个元素
// (*c)[1]将以a数组的第2个元素为首元素向后移动一位直接得到a数组的第3个元素
// (*c)[1] + 8将首字符't'的地址加8得到第9个字符'o'的地址
const char *f3() { return (*c)[1] + 8; }

// f4是一个函数，它不接收参数，并返回类型为const char *的表达式c[1][3] + 6的值
// 此时的c仍指向b数组的第3个元素
// c[1]以b数组的第3个元素为首元素向后移动一位直接得到b数组的第4个元素
// 而b数组的第4个元素存放的是a数组的第2个元素的地址
// c[1][3]以a数组的第2个元素为首元素向后移动三位直接得到a数组的第5个元素
// c[1][3] + 6将首字符'i'的地址加6得到第7个字符' '的地址
const char *f4() { return c[1][3] + 6; }

// F1是一个数组，数组中前四个元素都是一个函数指针，第5个元素为空指针
// 这个类型的函数指针，参数为空，返回类型为const char *
const char *(*F1[])() = {f1, f2, f3, f4, 0};

// F2是一根指针，指向的对象是函数指针
// 这个类型的函数指针，参数为空，返回类型为const char *
const char *(**F2)() = F1;

#include <stdio.h>
int main()
{
    // 定义并初始化整形变量i=0
    // 第1轮判定：F2指向的数组F1的第1个元素不为空，开始执行循环体
    // F1中第0个元素存放的是函数指针f1，加小括号调用函数指针指向的函数f1
    // 函数f1返回字符常量'H'的地址，以字符串的形式开始打印：'H','e','\0'
    // 整形变量i加1得到i=1，并开始第2轮判定，开始打印：'l','l','\0'
    // 整形变量i加1得到i=2，并开始第3轮判定，开始打印：'o','\0'
    // 整形变量i加1得到i=3，并开始第4轮判定，开始打印：' ','w','o','r','l','d','!','\0'
    // 整形变量i加1得到i=4，并开始第5轮判定：F2[4]即数组F1的第5个元素为空，循环结束
    for (int i = 0; F2[i]; i++)
    {
        printf("%s", F2[i]());
    }
    return 0;
}

// 总结：
// 字符串与数组的关系：字符串常量以字符数组的形式储存在全局区的常量区，
// 以它自身作为数组名，并且每个字符串尾自动加一个'\0'作为字符串结束标志。

// 数组与指针的关系：数组名是一个指向首元素的指针常量，只有两种情况例外：
// 其一，sizeof(数组名) 计算的是整个数组的大小，
// 其二，&数组名 得到的是整个数组的地址。
// p[i] 等价于 *(p + i) 等价于 i[p]，原因在于下标本质上是指针运算。
// 当数组作为函数参数传入时 T p[] 等价于 T *p， T为确定大小的数据类型。

// 函数与指针的关系：函数名=函数地址=函数指针，通过函数调用符()调用函数。
// 函数指针的类型为去掉函数名以外的东西，你可以这样定义函数类型Func：
// C：typedef void (*Func)(void)  C++：using Func = void (*)(void)